﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Linq;
using AutoMapper;

namespace NMA.Application
{
    using NMA.Domain.Model.Repository;
    using NMA.Infrastructure.EventAggregator;
    using NMA.Infrastructure.NHibernate.Service.Event;
    using NMA.Infrastructure.Logging;
    using NMA.Infrastructure.NHibernate.Container;
    using NMA.Infrastructure.DBC;
    using NMA.Infrastructure.NHibernate.DomainObject;
    using NMA.Domain.Model;
    using NMA.Domain.Shared;
    using NMA.Infrastructure.LambdaExpression;
    using NMA.Domain.Model.Service.Application;
    using NMA.Domain.Model.DTO;
    using NMA.Domain.Shared.Paging;
    using NMA.Infrastructure.NHibernate.Paging;
    using NMA.Domain.Model.Contract;
    using NMA.Infrastructure.NHibernate.CriteriaExpression;
    using NMA.Infrastructure.NHibernate.OrderByExpression;

    public class CategoryFacade : ICategoryFacade
    {
        #region variables

        private readonly ICategoryRepository _categoryRepository = null;
        private readonly IConsumer<AuditEvent> _handler = null;
        private readonly IAuditFactory _auditFactory = null;
        private readonly ICriteriaExpressionFactory _searchExpressionFactory = null;
        private readonly IOrderByExpressionFactory _orderByExpressionFactory = null;

        private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        #endregion

        #region ctors        

        public CategoryFacade() : this(IoC.GetInstance<ICategoryRepository>(),
                                       IoC.GetInstance<IConsumer<AuditEvent>>(),
                                       IoC.GetInstance<IAuditFactory>(),
                                       IoC.GetInstance<ICriteriaExpressionFactory>(),
                                       IoC.GetInstance<IOrderByExpressionFactory>())
        {
            UserName = "";
        }

        public CategoryFacade(ICategoryRepository categoryRepository, 
                              IConsumer<AuditEvent> handler, 
                              IAuditFactory auditFactory,
                              ICriteriaExpressionFactory searchExpressionFactory,
                              IOrderByExpressionFactory orderByExpressionFactory)
        {
            _categoryRepository = categoryRepository;
            _handler = handler;
            _auditFactory = auditFactory;
            _searchExpressionFactory = searchExpressionFactory;
            _orderByExpressionFactory = orderByExpressionFactory;
        }

        #endregion

        #region implement functions

        public string UserName { get; set; }

        public IEnumerable<CategoryDTO> All()
        {
            Check.Assert(_auditFactory != null, "AuditFactory object is null");

            try
            {
                var result = _categoryRepository.All();

                List<CategoryDTO> listDto = new List<CategoryDTO>();

                foreach (var item in result)
                {
                    listDto.Add(Mapper.Map<Category, CategoryDTO>((Category)item));
                }

                // Audit event
                _handler.Handle(_auditFactory.CreateGetAllCategoryEvent(UserName));

                return listDto;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public int CountAllCategory()
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");

            try
            {
                return _categoryRepository.CountAllCategory();
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            } 
        }

        public CategoryDTO GetById(Guid id)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(id != null, "Id is null");

            try
            {
                ICategory result = _categoryRepository.GetById(id);

                return Mapper.Map<Category, CategoryDTO>((Category)result);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public Guid Add(CategoryDTO entity)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(entity != null, "Category's object is null");

            try
            {
                object result = null;

                var entityMapped = Mapper.Map<CategoryDTO, Category>(entity);

                result = _categoryRepository.Add(entityMapped);

                return (Guid)result;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public void Remove(CategoryDTO entity)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(entity != null, "Category's object is null");

            try
            {
                var entityMapped = Mapper.Map<CategoryDTO, Category>(entity);

                _categoryRepository.Remove(entityMapped);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }  
        }

        public void Remove(Guid id)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(id != null, "Category's id is null");

            try
            {
                _categoryRepository.Remove(id);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public void Update(CategoryDTO entity)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(entity != null, "Category's entity is null");

            try
            {
                var entityMapped = Mapper.Map<CategoryDTO, Category>(entity);

                _categoryRepository.Update(entityMapped);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public IEnumerable<CategoryDTO> GetBy(Expression<Func<CategoryDTO, bool>> condition, Type identifyColumn)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(condition != null, "Condition for searching category is null");
            Check.Assert(identifyColumn != null, "Type is null");

            try
            {
                var lambda = LambdaExpressionHelper<ICategory, CategoryDTO>.Convert(condition, identifyColumn);

                var result = _categoryRepository.GetBy(lambda);

                IList<CategoryDTO> list = new List<CategoryDTO>();

                result.ToList().ForEach(x =>
                {
                    list.Add(Mapper.Map<Category, CategoryDTO>((Category)x));
                });

                return list;

            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public IPagedList<CategoryDTO> All(int pageIndex, int pageSize)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(pageIndex > 0, "Page Index is less than zero");
            Check.Assert(pageSize > 0, "Page Size is less than zero");

            var result = _categoryRepository.All(pageIndex, pageSize);
            int count = 0;

            IPagedList<CategoryDTO> list = new PagedList<CategoryDTO>();

            ((IEnumerable<ICategory>)result).ToList().ForEach(x => {
                list.Add(Mapper.Map<Category, CategoryDTO>((Category)x));
                count++;
            });

            list.TotalCount = count;
            list.PageIndex = pageIndex;
            list.PageSize = pageSize;

            return list;
        }

        public IPagedList<CategoryDTO> All(int pageIndex, int pageSize,
            CategorySearchCriteria searchModel,
            string orderBy,
            bool ascending)
        {
            Check.Assert(_categoryRepository != null, "Category Repository is null");
            Check.Assert(_searchExpressionFactory != null, "Search Expression Factory is null");
            Check.Assert(pageIndex > 0, "Page Index is less than zero");
            Check.Assert(pageSize > 0, "Page Size is less than zero");
            Check.Assert(searchModel != null, "Search criteria is null");

            Func<ICategory, bool> searchFunc = _searchExpressionFactory.CreateCategorySearchCriteria(searchModel);
            Func<ICategory, object> orderByFunc = _orderByExpressionFactory.CreateCategorySearchOrderBy(orderBy);
            var result = _categoryRepository.All(pageIndex, pageSize, searchFunc, orderByFunc, ascending);
            int count = 0;

            IPagedList<CategoryDTO> list = new PagedList<CategoryDTO>();

            ((IEnumerable<ICategory>)result).ToList().ForEach(x =>
            {
                list.Add(Mapper.Map<Category, CategoryDTO>((Category)x));
                count++;
            });

            list.TotalCount = count;
            list.PageIndex = pageIndex;
            list.PageSize = pageSize;

            return list;
        }

        #endregion
    }
}